package com.nkym.wypan.storage.core.local;/*
 * @ClassName LocalStorage
 * @Desc TODO
 * @Author 19637
 * @Date 2023/6/11 11:51
 * @Version 1.0
 */

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Comparator;
import java.util.List;

import java.io.OutputStream;

import java.io.*;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;


import com.nkym.wypan.storage.context.*;
import com.nkym.wypan.storage.core.AbstractStorageEngine;
import com.nkym.wypan.storage.response.UploadResult;
import com.nkym.wypan.storage.utils.FileUploadUtils;
import com.sun.org.slf4j.internal.Logger;
import com.sun.org.slf4j.internal.LoggerFactory;

public class LocalStorageEngine extends AbstractStorageEngine {

    private static final Logger log = LoggerFactory.getLogger(LocalStorageEngine.class);

    @Override
    protected UploadResult doStore(FileUpload fileUpload) throws IOException {
        String filename = fileUpload.getFilename();
        Long fileSize = fileUpload.getFileSize();
        InputStream stream = fileUpload.getStream();
        //生成文件存储路径
        String realPath = FileUploadUtils.generatePath(filename);
        writeToFile(new File(realPath), fileSize, stream);
        //返回结果
        UploadResult result = new UploadResult();
        result.setRealPath(realPath);
        return result;
    }

    @Override
    protected void doDownload(Download download) throws IOException {
        String realPath = download.getRealPath();
        OutputStream outputStream = download.getOutputStream();
        readFileToOutputStream(new File(realPath), outputStream);
    }

    protected UploadResult doChunkUpload(ChunkUpload chunkUpload) throws IOException {
        Integer chunkNumber = chunkUpload.getChunkNumber();
        String identifier = chunkUpload.getIdentifier();
        InputStream inputStream = chunkUpload.getInputStream();
        Long chunkFileSize = chunkUpload.getChunkFileSize();

        //生成真实路径
        String realPath = FileUploadUtils.generateChunkPath(identifier, chunkNumber);
        writeToFile(new File(realPath), chunkFileSize, inputStream);


        //返回结果
        UploadResult result = new UploadResult();
        result.setRealPath(realPath);
        return result;
    }

    @Override
    protected UploadResult doMerge(MergeRequest chunkRequest) throws IOException {
        List<Merge> mergeList = chunkRequest.getMergeList();
        String filename = chunkRequest.getFilename();
        mergeList.sort(Comparator.comparingInt(Merge::getChunkNumber));
        //根据分片列表合并文件
        String realPath = FileUploadUtils.generatePath(filename);
        File file = new File(realPath);
        if (!file.getParentFile().exists()) {
            file.mkdirs();
        }
        if (!file.exists()) {
            file.createNewFile();
        }
        for (Merge merge : mergeList) {
            //合并文件
            appendWrite(file.toPath(), new File(merge.getRealPath()).toPath());
        }

        //返回结果
        UploadResult result = new UploadResult();
        long length = new File(realPath).length();
        result.setRealPath(realPath);
        result.setFileSize(length);
        return result;
    }

    @Override
    protected void doPreview(PreviewRequest previewRequest) {
        String realPath = previewRequest.getRealPath();
        OutputStream outputStream = previewRequest.getStream();
        try {
            readFileToOutputStream(new File(realPath), outputStream);
        } catch (Exception e) {
            throw new RuntimeException("文件预览失败");
        }
    }

    @Override
    protected void doDownload(String realPath, OutputStream outputStream, Long startPosition, Long size) {
        try {
            readByteToOutputStream(realPath, outputStream, startPosition, size);
        } catch (IOException e) {
            log.error("{}", e);
        }
    }


    @Override
    protected void doDelete(DeleteRequest deleteRequest) {
        try {
            Files.delete(new File(deleteRequest.getRealPath()).toPath());
        } catch (IOException e) {
            throw new RuntimeException("IO异常");
        }
    }

    /**
     * ------------------------------------------private----------------------------------------------------------
     */
    private void readByteToOutputStream(String realPath, OutputStream outputStream, Long startPosition, Long size) throws IOException {
        File file = new File(realPath);
        if (!file.exists()) {
            throw new RuntimeException("文件不存在");
        }
        RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r");
        FileChannel fileChannel = randomAccessFile.getChannel();
        long len = fileChannel.transferTo(startPosition, size, Channels.newChannel(outputStream));
        // 关闭流
        outputStream.flush();
        outputStream.close();
        fileChannel.close();
        randomAccessFile.close();
        System.out.println("返回数据区间:【" + size + " " + startPosition + "-" + (startPosition + len) + "】");
    }

    private void appendWrite(Path target, Path source) throws IOException {
        Files.write(target, Files.readAllBytes(source), StandardOpenOption.APPEND);
    }

    private void readFileToOutputStream(File file, OutputStream outputStream) throws IOException {
        if (!file.exists()) {
            throw new RuntimeException("文件不存在");
        }
        FileChannel inputChannel = null;
        WritableByteChannel outputChannel = null;
        try {
            inputChannel = new RandomAccessFile(file, "rw").getChannel();
            outputChannel = Channels.newChannel(outputStream);
            inputChannel.transferTo(0, file.length(), outputChannel);
            outputStream.flush();
        } finally {
            if (inputChannel != null)
                inputChannel.close();
            if (outputChannel != null)
                outputChannel.close();
            if (outputStream != null)
                outputStream.close();
        }


    }

    private void writeToFile(File file, Long fileSize, InputStream stream) throws IOException {
        //如果目标文件的父文件夹不存在则创建
        if (!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();
        }
        RandomAccessFile accessFile = null;
        FileChannel outChannel = null;
        ReadableByteChannel inputChannel = null;
        try {
            accessFile = new RandomAccessFile(file, "rw");
            outChannel = accessFile.getChannel();
            inputChannel = Channels.newChannel(stream);
            outChannel.transferFrom(inputChannel, 0L, fileSize);
        } finally {
            if (accessFile != null)
                accessFile.close();
            if (outChannel != null)
                outChannel.close();
            if (inputChannel != null)
                inputChannel.close();
        }

    }


}
